﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Media;

namespace WpfApplication1
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        private const string BroadcastAddress = "255.255.255.255"; //UDP-Broadcast
        private const int Port = 54545; //Port zum Empfangen

        //Tritt ein, wenn ein Server-Broadcast empfangen wird
        private static UdpClient _sendingClient; //Zum Senden von Serverbroadcasts
        private static int _timeout; //Timeout bis in der Konsole connected wird
        private static string _serverAddress; //Addresse zu der in der Konsole connected wird
        private IPAddress _address; //IpAdresse des zu verbindenden Servers
        private Thread _onlineThread; //Thread zum Senden von Server-Broadcasts
        private UdpClient _receivingClient; //Zum Empfangen von Serverbroadcasts
        private Thread _receivingThread; //Thread zum Empfangen von Server-Broadcasts
        private IPAddress _serverIp; //IP-Adresse des lokalen PCs, falls ein Server erstellt wird

        public MainWindow()
        {
            InitializeComponent();
            _timeout = 20;
            _serverAddress = "";
            slider_timer.Value = 20;
        }

        /// <summary>
        /// Schreibt die AlterIWNet Konfigurationsdatei
        /// </summary>
        /// <param name="server">Spielserver</param>
        /// <param name="nickname">Nickname</param>
        private void WriteINI(string server, string nickname)
        {
            var writer = new StreamWriter("alterIWnet.ini");

            writer.WriteLine("[Configuration]");
            writer.WriteLine("Server=" + server);
            writer.WriteLine("WebHost=auto");
            writer.WriteLine("Nickname={0}", nickname);
            writer.WriteLine("[Dynamic]");
            writer.WriteLine("HackwarningShown=True");
            writer.WriteLine("ServerWarningShown=True");

            writer.Close();
        }

        /// <summary>
        /// Startet COD MW2
        /// </summary>
        /// <param name="startServer">Gibt an ob der Server auch gestartet werden soll</param>
        private void StartGame(bool startServer)
        {
            if (File.Exists("iw4mp.exe"))
            {
                var p = new Process {StartInfo = new ProcessStartInfo("iw4mp.exe")};
                p.Start();
            }
            else
            {
                MessageBox.Show("Die Datei iw4mp.exe wurde nicht gefunden!\n Ist der Loader im Spielverzeichnis?",
                                "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
            if (startServer)
            {
                if (File.Exists("IWNetServer.exe"))
                {
                    var p = new Process {StartInfo = new ProcessStartInfo("IWNetServer.exe")};
                    p.Start();
                }
                else
                {
                    MessageBox.Show(
                        "Die Datei IWNetServer.exe wurde nicht gefunden!\n Ist der Loader im Spielverzeichnis?");
                }
                if (File.Exists("iw4mphost.exe"))
                {
                    var p = new Process {StartInfo = new ProcessStartInfo("iw4mphost.exe")};
                    p.Start();
                }
            }
        }


        private static void Connect(int delay, string ip)
        {
            Thread.Sleep(delay*1000);
            WriteIntoConsole("connect " + ip);
        }


        private void ConnectConsole()
        {
            var t = new Thread(() => Connect(_timeout, _serverAddress));
            t.Start();
        }


        private void BtnOnlineClick(object sender, RoutedEventArgs e)
        {
            if (nickName.Text != "")
            {
                if (hostBox.IsChecked == false)
                {
                    WriteINI("server.alteriw.net", nickName.Text);
                    StartGame(false);
                }
                else
                {
                    MessageBox.Show("Spielen als Onlinehost funktioniert nicht!\nBitte Host unchecken!");
                }
            }

            else
            {
                MessageBox.Show("Bitte einen Nickname Eingeben");
            }
        }

        private void BtnNwClick(object sender, RoutedEventArgs e)
        {
            if (hostBox.IsChecked == true)
            {
                if (checkBox_serverBroadcast.IsChecked == true)
                {
                    if (nickName.Text != "" && IPAddress.TryParse(textBox_serverIP.Text, out _serverIp))
                    {
                        _serverAddress = textBox_serverIP.Text;
                        WriteINI(textBox_serverIP.Text, nickName.Text);
                        StartGame(true);
                        BroadcastServer();
                        ConnectConsole();
                    }
                    else if (nickName.Text != "")
                        MessageBox.Show("Bitte einen Nickname eingeben!");
                    else
                    {
                        MessageBox.Show("Bitte die gültige eigene IP-Adresse eingeben!");
                    }
                }
                else
                {
                    if (nickName.Text != "" && IPAddress.TryParse(textBox_serverIP.Text, out _serverIp))
                    {
                        _serverAddress = textBox_serverIP.Text;
                        WriteINI(textBox_serverIP.Text, nickName.Text);
                        StartGame(true);
                        ConnectConsole();
                    }
                    else if (nickName.Text != "")
                        MessageBox.Show("Bitte einen Nickname eingeben!");
                    else
                    {
                        MessageBox.Show("Bitte die gültige eigene IP-Adresse eingeben!");
                    }
                }
            }
            else
            {
                if (nickName.Text != "" && IPAddress.TryParse(serverBox.Text, out _address))
                {
                    _serverAddress = serverBox.Text;
                    WriteINI(serverBox.Text, nickName.Text);
                    StartGame(false);
                    ConnectConsole();
                }
                else
                {
                    MessageBox.Show("Bitte Nickname und IP-Adresse eingeben!");
                }
            }
        }


        private void ServerBoxGotFocus(object sender, RoutedEventArgs e)
        {
            serverBox.Text = "";
            serverBox.FontSize = 16;
            serverBox.Foreground = Brushes.Black;
        }

        private void ButtonFindServerClick(object sender, RoutedEventArgs e)
        {
            if (button_findServer.Content.ToString() == "Server finden")
            {
                _receivingClient = new UdpClient(Port);
                ThreadStart start = Receive;
                _receivingThread = new Thread(start) {IsBackground = true};
                _receivingThread.Start();

                button_findServer.Content = "Abbrechen";
            }
            else
            {
                _receivingThread.Abort();
                _receivingClient.Close();
                button_findServer.Content = "Server finden";
            }
        }

        /// <summary>
        /// Empfängt UDP-Pakete
        /// </summary>
        private void Receive()
        {
            var endpoint = new IPEndPoint(IPAddress.Broadcast, Port);
            IpReceived ipDelegate = AddressReceived;

            while (true)
            {
                byte[] data = _receivingClient.Receive(ref endpoint);
                string messageEnc = Encoding.ASCII.GetString(data);

                var encoder = new UTF8Encoding();
                Decoder utf8Decode = encoder.GetDecoder();
                byte[] todecodeByte = Convert.FromBase64String(messageEnc);
                int charCount = utf8Decode.GetCharCount(todecodeByte, 0, todecodeByte.Length);
                var decodedChar = new char[charCount];
                utf8Decode.GetChars(todecodeByte, 0, todecodeByte.Length, decodedChar, 0);
                var message = new String(decodedChar);
                Dispatcher.Invoke(ipDelegate, message);
            }
        }

        /// <summary>
        /// Tritt ein wenn eine IP-Adresse empfangen wird
        /// </summary>
        /// <param name="message"></param>
        private void AddressReceived(string message)
        {
            if (IPAddress.TryParse(message, out _address))
            {
                serverBox.Text = message;
                _receivingThread.Abort();
                _receivingClient.Close();
                serverBox.Background = Brushes.LightGreen;
                button_findServer.Content = "Server finden";
            }
        }

        private void HostBoxChecked(object sender, RoutedEventArgs e)
        {
            serverBox.IsEnabled = false;
            button_findServer.IsEnabled = false;
            checkBox_serverBroadcast.IsEnabled = true;
            textBox_serverIP.Opacity = 1;
        }

        private void HostBoxUnchecked(object sender, RoutedEventArgs e)
        {
            serverBox.IsEnabled = true;
            button_findServer.IsEnabled = true;
            checkBox_serverBroadcast.IsEnabled = false;
            textBox_serverIP.Opacity = 0;
        }

        private void TextBox1GotFocus(object sender, RoutedEventArgs e)
        {
            textBox_serverIP.Text = "";
        }

        /// <summary>
        /// Schreibt einen Text in die Konsole und drückt anschließend Return
        /// </summary>
        /// <param name="text"></param>
        private static void WriteIntoConsole(string text)
        {
            IntPtr consoleWindow = FindWindow("IW4 WinConsole", "aIW Console");
            IntPtr textBoxConsole = FindWindowEx(consoleWindow.ToInt32(), 0, "Edit", null);
            var nothing = new IntPtr(0);

            SendMessage(textBoxConsole, WM_SETTEXT, nothing, text); //Schreibt den Text
            PostMessage(textBoxConsole, WM_KEYDOWN, VK_RETURN, 0); //Drückt enter
        }

        private void WindowClosing(object sender, CancelEventArgs e)
        {
            try
            {
                _receivingThread.Abort();
                _receivingClient.Close();
                _sendingClient.Close();
                _onlineThread.Abort();
            }
            catch
            {
            }
        }

        private void SliderTimerValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            _timeout = (int) slider_timer.Value;
            slider_timer.Value = _timeout;
            label_timer.Content = "Timeout für Connect: " + slider_timer.Value;
        }

        #region WindowAccess //Zum Zugreifen auf andere Fenster und deren Controls

        //FindWindow
        private const int WM_SETTEXT = 0xC;
        private const int VK_RETURN = 0x0D;
        private const int WM_KEYDOWN = 0x0100;

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowTitle);

        //FindWindowEx
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern IntPtr FindWindowEx(int parentHandle, int childAfter, string lpClassName,
                                                  string lpWindowTitle);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr handle, UInt32 msg, IntPtr wParam, string lParam);

        [DllImport("user32.dll")]
        private static extern bool PostMessage(IntPtr hWnd, UInt32 msg, int wParam, int lParam);

        #endregion

        #region Schickt Server-Broadccasts

        private void BroadcastServer()
        {
            if (!IPAddress.TryParse(textBox_serverIP.Text, out _serverIp)) return;

            _sendingClient = new UdpClient(BroadcastAddress, Port) {EnableBroadcast = true};
            ThreadStart onlineThreadStart = Broadcast;
            _onlineThread = new Thread(onlineThreadStart) {IsBackground = true};
            _onlineThread.Start();
        }

        private void Broadcast()
        {
            while (true)
            {
                string message = _serverIp.ToString();
                byte[] encMsgByte = Encoding.UTF8.GetBytes(message);
                string encodedMsg = Convert.ToBase64String(encMsgByte);

                byte[] data = Encoding.ASCII.GetBytes(encodedMsg);
                _sendingClient.Send(data, data.Length);
                Thread.Sleep(3000);
            }
        }

        #endregion

        #region Nested type: IPReceived

        private delegate void IpReceived(string message);

        #endregion
    }
}